package com.gsbelarus.gedemin.salary.service;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gsbelarus.gedemin.salary.R;
import com.gsbelarus.gedemin.salary.database.RealmHelper;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgModel;
import com.gsbelarus.gedemin.salary.entity.model.SyncGdMsgStateModel;
import com.gsbelarus.gedemin.salary.fragment.SettingsFragment;
import com.gsbelarus.gedemin.salary.util.WebServiceManager;
import com.orhanobut.logger.Logger;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import io.realm.Realm;
import io.realm.RealmResults;

interface CallableFunc<TArg1, TArg2> {
    void onResponse(TArg1 val1, TArg2 val2) throws Exception;
}

public class SendMsgDataService extends IntentService implements ExclusionStrategy {

    public static final String SEND_GD_MSG_STATES_ACTION    = "com.gsbelarus.gedemin.salary.service.send_gd_msg_states_action";

    public static final String SEND_GD_MSG_ACTION           = "com.gsbelarus.gedemin.salary.service.send_gd_msg_action";
    public static final String EXTRA_KEY_GD_MSG             = "gd_msg";

    public static final String SEND_GD_MSG_BROADCAST_ACTION = "com.gsbelarus.gedemin.salary.service.send_gd_msg_broadcast_action";
    public static final String EXTRA_KEY_GD_MSG_WAS_SENT    = "gd_msg_was_sent";

    public static final String REQUEST_PARAM_TOKEN              = "token";
    public static final String REQUEST_PARAM_EMPL_ID            = "empl_id";
    public static final String REQUEST_PARAM_GD_MSG             = "gd_msg";
    public static final String REQUEST_PARAM_GCM_REG_ID         = "gcm_reg_id";
    public static final String REQUEST_PARAM_GD_MSG_STATE_ARRAY = "gd_msg_state_array";
    public static final String REQUEST_PARAM_LAST_SYNC_DATE     = "last_sync_date";
    public static final String REQUEST_PARAM_LAST_SYNC_MSG_DATE = "last_sync_msg_date";
    public static final String REQUEST_PARAM_LAST_SYNC_KEY      = "last_sync_key";
    public static final String REQUEST_PARAM_PERSONAL_NUMBER    = "personal_number";
    public static final String REQUEST_PARAM_LIST_NUMBER        = "list_number";
    public static final String REQUEST_PARAM_FIRSNAME           = "firstname";
    public static final String REQUEST_PARAM_MIDDLENAME         = "middlename";
    public static final String REQUEST_PARAM_SURNAME            = "surname";

    public static final String REQUEST_TOKEN_SEND_GD_MSG        = "gdmnSalarySendMsg";
    public static final String REQUEST_TOKEN_SEND_GD_MSG_STATES = "gdmnSalarySendMsgStates";

    private SyncNotification syncNotification;
    private Context context;

    public SendMsgDataService() {
        super(SendMsgDataService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        context = getApplicationContext();
        syncNotification = new SyncNotification(context);

        startForeground(SyncNotification.SEND_NOTIFICATION_ID, syncNotification.getSendNotification(context.getString(R.string.notification_sending)));

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        RealmHelper realmHelper = new RealmHelper(Realm.getInstance(context));

        if (intent.getAction().equalsIgnoreCase(SEND_GD_MSG_ACTION)) {

            final GdMsgModel gdMsgModel = (GdMsgModel) intent.getExtras().getSerializable(EXTRA_KEY_GD_MSG);

            if (prefs.getBoolean(SettingsFragment.WORK_MODE, false)) {
                GsonBuilder builder = new GsonBuilder();
                builder.setDateFormat(SyncService.SERVER_DATE_TIME_FORMAT);
                Gson gson = builder.excludeFieldsWithoutExposeAnnotation().setExclusionStrategies(this).create();
                String jsonMsg = gson.toJson(gdMsgModel);

                Map<String, String> requestParams = new HashMap<>();
                requestParams.put(REQUEST_PARAM_TOKEN, REQUEST_TOKEN_SEND_GD_MSG);
                requestParams.put(REQUEST_PARAM_EMPL_ID, realmHelper.getSyncInfo(true).getEmplId());
                if (realmHelper.getSyncInfo(true).getGcmRegistrationId() != null && !realmHelper.getSyncInfo(true).getGcmRegistrationId().isEmpty())
                    requestParams.put(REQUEST_PARAM_GCM_REG_ID, realmHelper.getSyncInfo(true).getGcmRegistrationId());
                requestParams.put(REQUEST_PARAM_GD_MSG, jsonMsg);

                Map<String, String> headers = new HashMap<>();
                ///headers.put("Connection", "close");  // disable keep-alive
                headers.put("Content-Type", "application/json; charset=utf-8");

                callWebService(
                        new CallableFunc<RealmHelper, WebServiceManager>() {
                            @Override
                            public void onResponse(RealmHelper realmHelper, WebServiceManager webServiceManager) throws Exception {
                                if (webServiceManager.hasResponseStatusOK()) {
                                    HashMap<String, String> response = webServiceManager.parseResponse();
                                    if (response.containsKey(SyncService.RESPONSE_PARAM_ERROR_MSG)) {
                                        Exception exception = new Exception("SendMsgDataService sending GD_MSG: " + response.get(SyncService.RESPONSE_PARAM_ERROR_MSG));
                                        Logger.e(exception, exception.getMessage());
                                        //Crashlytics.getInstance().core.logException(new Exception("SendMsgDataService sending GD_MSG: " + response.get(SyncService.RESPONSE_PARAM_ERROR_MSG)));
                                        throw exception;
                                    }

                                    if (response.containsKey("gdmsg_id") && response.containsKey("gdmsg_timestamp")) {
                                        gdMsgModel.setUid(Integer.valueOf(response.get("gdmsg_id")));
                                        gdMsgModel.setTimestamp(new SimpleDateFormat(SyncService.SERVER_DATE_TIME_FORMAT).parse(response.get("gdmsg_timestamp")));
                                    } else {
                                        Exception exception = new Exception("gdmsg_id или gdmsg_timestamp не найдены");
                                        Logger.e(exception, exception.getMessage());
                                        //Crashlytics.getInstance().core.logException(new Exception("gdmsg_id или gdmsg_timestamp не найдены"));
                                        throw exception;
                                    }

                                    realmHelper.save(gdMsgModel, true);
                                    Intent broadcastIntent = new Intent(SEND_GD_MSG_BROADCAST_ACTION);
                                    broadcastIntent.putExtra(EXTRA_KEY_GD_MSG_WAS_SENT, true);
                                    sendBroadcast(broadcastIntent);
                                }
                            }
                        },
                        WebServiceManager.buildRequestUrl("http", prefs.getString(SettingsFragment.ADDRESS_SERVER, ""), requestParams),
                        headers,
                        realmHelper
                );
            } else {
                realmHelper.save(gdMsgModel, true);
                Intent broadcastIntent = new Intent(SEND_GD_MSG_BROADCAST_ACTION);
                broadcastIntent.putExtra(EXTRA_KEY_GD_MSG_WAS_SENT, true);
                sendBroadcast(broadcastIntent);
            }
        } else if (intent.getAction().equalsIgnoreCase(SEND_GD_MSG_STATES_ACTION)) { ///TODO
            if (prefs.getBoolean(SettingsFragment.WORK_MODE, false)) {
                RealmResults<SyncGdMsgStateModel> states = realmHelper.getAll(SyncGdMsgStateModel.class);

                if (states.size() > 0) {
                    GsonBuilder builder = new GsonBuilder();
                    builder.setDateFormat(SyncService.SERVER_DATE_TIME_FORMAT);
                    Gson gson = builder.excludeFieldsWithoutExposeAnnotation().setExclusionStrategies(this).create();
                    String jsonData = "";

                    for (final SyncGdMsgStateModel state : states) {
                        jsonData += gson.toJson(state);
                    }

                    Map<String, String> requestParams = new HashMap<>();
                    requestParams.put(REQUEST_PARAM_TOKEN, REQUEST_TOKEN_SEND_GD_MSG_STATES);
                    requestParams.put(REQUEST_PARAM_EMPL_ID, realmHelper.getSyncInfo(true).getEmplId());
                    if (realmHelper.getSyncInfo(true).getGcmRegistrationId() != null && !realmHelper.getSyncInfo(true).getGcmRegistrationId().isEmpty())
                        requestParams.put(REQUEST_PARAM_GCM_REG_ID, realmHelper.getSyncInfo(true).getGcmRegistrationId());
                    requestParams.put(REQUEST_PARAM_GD_MSG_STATE_ARRAY, Uri.encode(jsonData, "utf-8"));

                    Map<String, String> headers = new HashMap<>();
                    ///headers.put("Connection", "close");  // disable keep-alive
                    headers.put("Content-Type", "application/json; charset=utf-8");

                    callWebService(
                            new CallableFunc<RealmHelper, WebServiceManager>() {
                                @Override
                                public void onResponse(RealmHelper realmHelper, WebServiceManager webServiceManager) throws Exception {
                                    if (webServiceManager.hasResponseStatusOK()) {
                                        HashMap<String, String> response = webServiceManager.parseResponse();
                                        if (response.containsKey(SyncService.RESPONSE_PARAM_ERROR_MSG)) {
                                            Exception exception = new Exception("SendMsgDataService sending GD_MSG_STATES: " + response.get(SyncService.RESPONSE_PARAM_ERROR_MSG));
                                            Logger.e(exception, exception.getMessage());
                                            //Crashlytics.getInstance().core.logException(exception);
                                            throw exception;
                                        } else {
                                            realmHelper.deleteAll(SyncGdMsgStateModel.class, true);
                                        }
                                    }
                                }
                            },
                            WebServiceManager.buildRequestUrl("http", prefs.getString(SettingsFragment.ADDRESS_SERVER, ""), requestParams),
                            headers,
                            realmHelper
                    );
                }
            }
        }

        stopForeground(true);
    }

    public void callWebService(CallableFunc<RealmHelper, WebServiceManager> c, String uri, Map<String, String> headers, RealmHelper realmHelper) {
        WebServiceManager webServiceManager = new WebServiceManager();
        try {
            webServiceManager.openConnection(uri);

            webServiceManager.sendGetRequest(headers); //TODO POST

            c.onResponse(realmHelper, webServiceManager);

        } catch (Exception e) {
            Logger.e(e, e.getMessage());
            //Crashlytics.getInstance().core.logException(e);
            stopForeground(true);
            showFinishError();
        } finally {
            webServiceManager.closeConnection();
            realmHelper.getRealm().close();
        }
    }

    public void showFinishError() {
        syncNotification.showErrorNotification(context.getString(R.string.notification_sending_error));
    }

    /**
     * @param f the field object that is under test
     * @return true if the field should be ignored; otherwise false
     */
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return false;
    }

    /**
     * @param clazz the class object that is under test
     * @return true if the class should be ignored; otherwise false
     */
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}
